#include "md5.h"
#include "duktape.h"
#include "utils.h"
#include "log.h"
//[]
static int constructor(duk_context* ctx){
    duk_push_this(ctx);//[this]
    MD5_CTX* md5ctx = (MD5_CTX*)malloc(sizeof(MD5_CTX));
    if(!md5ctx){
        duk_error(ctx,DUK_ERR_REFERENCE_ERROR,"md5 constructor alloc MD5_CTX failed");
    }
    MD5_Init(md5ctx);
    duk_push_pointer(ctx,md5ctx);//[this,md5ctx]
    duk_put_prop_string(ctx,0,DUK_HIDDEN_SYMBOL("cobj"));//[this]
    return 1;
}
//[]
static int reset(duk_context* ctx){
    duk_push_this(ctx);//[this]
    duk_get_prop_string(ctx,-1,DUK_HIDDEN_SYMBOL("cobj"));//[this,cobj]
    MD5_CTX* md5ctx = (MD5_CTX*)duk_get_pointer(ctx,-1);
    duk_pop(ctx);//[this]
    if(!md5ctx){
        duk_error(ctx,DUK_ERR_REFERENCE_ERROR,"md5 md5ctx is null");
    }
    MD5_Init(md5ctx);
    return 0;
}
//[buffer]
static int update(duk_context* ctx){
    if(!duk_is_buffer_data(ctx,-1)){
        duk_error(ctx,DUK_ERR_SYNTAX_ERROR,"md5 update arg0 is not buffer");
    }
    duk_push_this(ctx);//[buffer,this]
    duk_get_prop_string(ctx,-1,DUK_HIDDEN_SYMBOL("cobj"));//[buffer,this,cobj]
    MD5_CTX* md5ctx = (MD5_CTX*)duk_get_pointer(ctx,-1);
    duk_pop_2(ctx);//[buffer]
    if(md5ctx){
        int bufSize = 0;
        void * buf = duk_get_buffer_data(ctx,-1,&bufSize);
        MD5_Update(md5ctx,buf,bufSize);
    }
    return 0;
}
//return hex
//[]
static int final(duk_context* ctx){
    duk_push_this(ctx);//[this]
    duk_get_prop_string(ctx,-1,DUK_HIDDEN_SYMBOL("cobj"));//[this,cobj]
    MD5_CTX* md5ctx = (MD5_CTX*)duk_get_pointer(ctx,-1);
    duk_pop_2(ctx);//[]
    if(md5ctx){
        int size = 0;
        duk_push_fixed_buffer(ctx,16);//[buffer]
        uint8_t* digest = (uint8_t*)duk_get_buffer(ctx,-1,&size);
        if(!digest){
            duk_error(ctx,DUK_ERR_REFERENCE_ERROR,"md5 final malloc failed");
        }
        MD5_Final(digest,md5ctx);
        duk_hex_encode(ctx,-1);//[buffer]
    }
    return 1;
}
static duk_function_list_entry funcs[] = {
    {"iot.crypto.MD5",constructor,0},
    {"reset",reset,0},
    {"update",update,1},
    {"final",final,0},
    {0/*name*/,0/*c function*/,0 /*nargs*/}
};
int md5_init(duk_context* ctx){
    register_class(ctx,funcs);    
    return 0;
}